import type { EnvironmentConfigurationRuntime } from '@src/lib/constants'

export function generateDomainsFromBaseDomain(baseDomain: string) {
  return {
    API_URL: `https://api.${baseDomain}`,
    SITE_URL: `https://${baseDomain}`,
    KITTYCAD_WEBSOCKET_URL: `wss://api.${baseDomain}/ws/modeling/commands`,
    MLEPHANT_WEBSOCKET_URL: `wss://api.${baseDomain}/ws/ml/copilot`,
    APP_URL: `https://app.${baseDomain}`,
  }
}

export type EnvironmentVariables = {
  readonly NODE_ENV: string | undefined
  readonly VITE_ZOO_BASE_DOMAIN: string | undefined
  readonly VITE_ZOO_API_BASE_URL: string | undefined
  readonly VITE_KITTYCAD_WEBSOCKET_URL: string | undefined
  readonly VITE_MLEPHANT_WEBSOCKET_URL: string | undefined
  readonly VITE_ZOO_API_TOKEN: string | undefined
  readonly VITE_ZOO_SITE_BASE_URL: string | undefined
  readonly VITE_ZOO_SITE_APP_URL: string | undefined
  readonly POOL: string | undefined
}

/** Store the environment in memory to be accessed during runtime */
let ENVIRONMENT: EnvironmentConfigurationRuntime | null = null

/** Update the runtime environment */
export const updateEnvironment = (environment: string | null) => {
  if (environment === '') {
    console.log('reject updating environment: value is the empty string.')
    return
  }

  if (environment === null) {
    ENVIRONMENT = null
  } else {
    if (ENVIRONMENT) {
      ENVIRONMENT.domain = environment
    } else {
      ENVIRONMENT = {
        domain: environment,
        pool: '',
      }
    }
  }
  console.log('updating environment', environment)
}

export const updateEnvironmentPool = (
  environmentName: string,
  pool: string
) => {
  if (environmentName === '') {
    console.log(
      'reject updating pool,  environment: value is the empty string.'
    )
    return
  }
  if (!ENVIRONMENT) return
  if (ENVIRONMENT.domain === environmentName) {
    ENVIRONMENT.pool = pool
  }
}

// Do not export the entire Environment! Use env()
const getEnvironmentFromThisFile = (baseDomain: string) => {
  return (
    ENVIRONMENT || {
      domain: baseDomain,
      pool: '',
    }
  )
}

export const viteEnv = () => {
  // It turns out import.meta.env is a really fucky env var passing method.
  // It's purely generated by Vite and nothing else.
  // For Jest tests, we use babel to deal with it (it's a Syntax error otherwise)
  // @ts-ignore: TS1343
  return import.meta.env
}

export const windowElectronProcessEnv = () => {
  return typeof window !== 'undefined' && typeof window.electron !== 'undefined'
    ? window?.electron?.process?.env
    : undefined
}

export const processEnv = () => {
  if (typeof process === 'undefined') {
    // Web, no window.process or process
    return undefined
  }
  return process.env
}

/**
 * This function will work in any runtime. Note that we shouldn't be using this for any values outside of the
 * EnvironmentVariables type. This is not going to replace process.env.
 *
 * Vite -> node.js -> bridge -> javascript
 * We want to have the node.js and javascript runtime share the same code for getting these important configurations.
 */
export default (): EnvironmentVariables => {
  // Compute the possible environment variables, order operation is important
  // runtime (TODO) > process.env > window.electron.process.env > import.meta.env
  const viteOnly = viteEnv()
  const windowElectronProcessEnvOnly = windowElectronProcessEnv()
  const processEnvOnly = processEnv()
  const env = processEnvOnly || windowElectronProcessEnvOnly || viteOnly

  let BASE_DOMAIN = env.VITE_ZOO_BASE_DOMAIN
  let {
    API_URL,
    SITE_URL,
    KITTYCAD_WEBSOCKET_URL,
    MLEPHANT_WEBSOCKET_URL,
    APP_URL,
  } = generateDomainsFromBaseDomain(BASE_DOMAIN)
  let pool = ''

  /**
   * If you are desktop, see if you have any runtime environment which can be read from disk and
   * populated during the sign in workflow.
   * A built binary will allow the user to sign into different environments on the desktop
   */
  const environment = getEnvironmentFromThisFile(BASE_DOMAIN)
  if (
    typeof window !== 'undefined' &&
    window.electron &&
    environment &&
    environment.domain
  ) {
    const environmentDomains = generateDomainsFromBaseDomain(environment.domain)
    API_URL = environmentDomains.API_URL
    SITE_URL = environmentDomains.SITE_URL
    KITTYCAD_WEBSOCKET_URL = environmentDomains.KITTYCAD_WEBSOCKET_URL
    MLEPHANT_WEBSOCKET_URL = environmentDomains.MLEPHANT_WEBSOCKET_URL
    APP_URL = environmentDomains.APP_URL
    pool = environment && environment.pool ? environment.pool : ''
    BASE_DOMAIN = environment.domain
  }

  /**
   * Allow .env.development.local to override the WebSocket URLs
   */
  if (
    env.VITE_KITTYCAD_WEBSOCKET_URL &&
    env.VITE_KITTYCAD_WEBSOCKET_URL !== 'undefined'
  ) {
    KITTYCAD_WEBSOCKET_URL = env.VITE_KITTYCAD_WEBSOCKET_URL
  }
  if (
    env.VITE_MLEPHANT_WEBSOCKET_URL &&
    env.VITE_MLEPHANT_WEBSOCKET_URL !== 'undefined'
  ) {
    MLEPHANT_WEBSOCKET_URL = env.VITE_MLEPHANT_WEBSOCKET_URL
  }

  const environmentVariables: EnvironmentVariables = {
    NODE_ENV: (env.NODE_ENV as string) || viteOnly.MODE || undefined,
    VITE_ZOO_BASE_DOMAIN: BASE_DOMAIN || undefined,
    VITE_ZOO_API_BASE_URL: API_URL || undefined,
    VITE_KITTYCAD_WEBSOCKET_URL: KITTYCAD_WEBSOCKET_URL || undefined,
    VITE_MLEPHANT_WEBSOCKET_URL: MLEPHANT_WEBSOCKET_URL || undefined,
    VITE_ZOO_API_TOKEN:
      (env.VITE_ZOO_API_TOKEN as string) ||
      (env.VITE_KITTYCAD_API_TOKEN as string) ||
      undefined,
    VITE_ZOO_SITE_BASE_URL: SITE_URL || undefined,
    VITE_ZOO_SITE_APP_URL: APP_URL || undefined,
    POOL: pool, // TODO: Rename to ENGINE_POOL to be more descriptive
  }

  return environmentVariables
}
